#pragma once
#include "HuffmanTree.h"
#include <string>
#include <assert.h>
#include<algorithm>
typedef long long  LongType;
struct CharInfo
{
	unsigned char  _ch;   //这样定义压缩汉子是就不会出现乱码问题了
	LongType       _CharCount;   //字符出现次数
	string         _code;   //huffman编码
	CharInfo(LongType CharCount = 0)
		:_ch(0)
		, _CharCount(CharCount)
	{
	}
	CharInfo operator+(const CharInfo& c)
	{
		CharInfo tmp;
		tmp._CharCount = _CharCount + c._CharCount;
		return tmp;
	}
	bool operator != (const CharInfo c)
	{
		return _CharCount != c._CharCount;
	}
	bool operator < (const CharInfo& c)const
	{
		return _CharCount < c._CharCount;
	}
};
class FileCompress
{
public:
	FileCompress()
	{
		for (int i = 0; i < 256;i++)// 读取字符对应ASCII表，将字符存在数组下标与字符ASCLL码相等的位置
		{
			_str[i]._ch = i;
		}
	}
	void Compress(const char* filename)
	{
		assert(filename);
		FILE* fout = fopen(filename,"rb");//   使用二进制形式读，否则读不到汉字
		assert(fout);
		int ch = fgetc(fout);
		while (!feof(fout))//未读到文件结尾
		{
			_str[ch]._CharCount++;   //统计次数
			ch = fgetc(fout);        //继续向后读字符
		}
		CharInfo  invalid(0);    //定义一个非法值，当CharCount=0时就相当于一个非法值，无需根据权值构建Huffman树
		Huffman<CharInfo> hf(_str,256,invalid);  //根据字符次数构建huffman树
		string  code;
		_GetHuffmanCode(hf.GetNode(), code);
		string CompressFilename = filename;       //将编码写入压缩文件中
		CompressFilename += ".compress";
		FILE* Input = fopen(CompressFilename.c_str(),"wb");
		assert(Input);
		fseek(fout,0,SEEK_SET);
 	char Inputch = 0;   //写入文件压缩编码 
		int size = 0;
		ch = fgetc(fout);
		while (!feof(fout))
		{
			string& code = _str[ch]._code;
			for (size_t i = 0; i < code.size();++i)
			{
				 Inputch <<=1;
			 
				if (code[i]=='1')
				{
					 Inputch |= 1;
					 
				}
				++size;
				if (size == 8)
				{
					 fputc(Inputch, Input);
					 
					size = 0;
					Inputch = 0;
				}
			}
			ch = fgetc(fout);
	}
		if (size>0)
		{
		 
		 Inputch <<= 8 - size;
			 fputc(Inputch,Input);
 
		}
		fclose(fout);
		fclose(Input);
		//配置文件
		string ConfigFilename = filename;
		ConfigFilename += ".config";
		FILE* FInConfig = fopen(ConfigFilename.c_str(),"wb");
		string line;
		for (size_t i = 0; i < 256; ++i)
		{
		if (_str[i]._CharCount!=0)
		{
			line +=_str[i]._ch;
			line += ',';
			char buff[20] = { 0 };
			line += _itoa(_str[i]._CharCount, buff, 10);//转换为10进制
			line += '\n';
			fwrite(line.c_str(),1,line.size(),FInConfig);  
			line.clear();  
		}
		}
		fclose(FInConfig);
	}
	void Uncompress(const char* filename)
	{
		string ConfigFile = filename;
		ConfigFile += ".config";
		FILE* foutconfig = fopen(ConfigFile.c_str(), "rb");
		assert(foutconfig);
		string line;
		while (ReadLine(foutconfig, line))
		{
			if (line.empty())
			{
				line += '\n';
				continue;
			}
			else
			{
				unsigned char ch = line[0];
				_str[ch]._CharCount = atoi(line.substr(2).c_str());
				line.clear();
			}
		}
		CharInfo invalid(0);
		Huffman<CharInfo> hf(_str, 256, invalid);
		string uncompressfilename = filename;
		uncompressfilename += ".uncompress";
		FILE* fin = fopen(uncompressfilename.c_str(), "wb");
		assert(fin);
		string compressfilename = filename;
		compressfilename += ".compress";
		FILE* fout = fopen(compressfilename.c_str(), "rb");
		assert(fout);
		HuffmanNode<CharInfo> *root = hf.GetNode();
		LongType ChLen = root->_weight._CharCount;
		HuffmanNode<CharInfo> *cur = root;
		int pos = 7;
		int ch = fgetc(fout);
		while (ChLen)
		{
			if (ch&(1<<pos))
			{
				cur = cur->_right;
			}
			else
				cur = cur->_left;
			--pos;
			if (cur->_left==NULL&&cur->_right==NULL)
			{
				fputc(cur->_weight._ch, fin);
				cur = root;
				--ChLen;
				if (ChLen==0)
                 break;
			}
			if (pos==-1)
			{
				ch = fgetc(fout);
				pos = 7;
			}
		}
		fclose(foutconfig);
		fclose(fin);
		fclose(fout);
	}
protected:
	void _GetHuffmanCode(HuffmanNode<CharInfo>* root,string& code)
	{
   if (root==NULL)
   {
	   return;
   }
   if (root->_left==NULL&&root->_right==NULL)
   {
	   _str[root->_weight._ch]._code = code;
   }
   _GetHuffmanCode(root->_left, code + '0');
   _GetHuffmanCode(root->_right, code + '1');
	}
	bool ReadLine(FILE *fout, string& line)
	{
		int ch = fgetc(fout);
		if (feof(fout))
		{
			return false;
		}
		while (!feof(fout)&&ch!='\n')
		{
			line += ch;
			ch = fgetc(fout);
		}
		return true;
	}
protected:
	CharInfo _str[256];
};
